﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.Diagnostics;
using System.Globalization;
using System.Text.RegularExpressions;

namespace LotroData
{
    /// <summary>
    /// Contains information about the Profession infomation for a character
    /// </summary>
    public struct LotroCharacterProfession : IEquatable<LotroCharacterProfession>
    {
        /// <summary>
        /// Gets an empty <see cref="LotroCharacterProfession"/> structure.
        /// </summary>
        public static readonly LotroCharacterProfession Empty = new LotroCharacterProfession(LotroProfession.Unknown, 0, 0);

        /// <summary>
        /// Gets or sets the <see cref="LotroProfession"/> value associated with this instance.
        /// </summary>
        /// <value>A <see cref="LotroProfession"/> value representing the profession.</value>
        public LotroProfession Profession { get; set; }

        /// <summary>
        /// Gets or sets the mastery level of the profession, in a range from 0 to 6.
        /// </summary>
        /// <value>An <c>int</c> representing the mastery level for this profession.</value>
        public int MasteryLevel { get; set; }

        /// <summary>
        /// Gets or sets the proficiency level of the profession, in a range from 0 to 6.
        /// </summary>
        /// <value>An <c>int</c> representing the proficiency level for this profession.</value>
        public int ProficiencyLevel { get; set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="LotroCharacterProfession"/> struct.
        /// </summary>
        /// <param name="profession">A <see cref="LotroProfession"/> value representing the profession.</param>
        /// <param name="proficiency">An <c>int</c> representing the proficiency level for this profession.</param>
        /// <param name="mastery">An <c>int</c> representing the mastery level for this profession.</param>
        public LotroCharacterProfession(LotroProfession profession, int proficiency, int mastery)
            : this()
        {
            if (proficiency < 0 || proficiency > 6) throw new ArgumentOutOfRangeException("proficiency");
            if (mastery < 0 || mastery > 6) throw new ArgumentOutOfRangeException("mastery");

            this.Profession = profession;
            this.ProficiencyLevel = proficiency;
            this.MasteryLevel = mastery;
        }

        /// <summary>
        /// Determines whether the specified <see cref="System.Object"/> is equal to this instance.
        /// </summary>
        /// <param name="obj">The <see cref="System.Object"/> to compare with this instance.</param>
        /// <returns>
        /// 	<c>true</c> if the specified <see cref="System.Object"/> is equal to this instance; otherwise, <c>false</c>.
        /// </returns>
        public override bool Equals(object obj)
        {
            if (obj == null || this.GetType() != obj.GetType())
                return false;

            return Equals((LotroCharacterProfession)obj);
        }

        /// <summary>
        /// Determines whether the specified <see cref="LotroCharacterProfession"/> is equal to this instance.
        /// </summary>
        /// <param name="other">The <see cref="LotroCharacterProfession"/> to compare with this instance.</param>
        /// <returns>
        /// 	<c>true</c> if the specified <see cref="LotroCharacterProfession"/> is equal to this instance; otherwise, <c>false</c>.
        /// </returns>
        public bool Equals(LotroCharacterProfession other)
        {
            if (this.Profession != other.Profession)
                return false;
            if (this.ProficiencyLevel != other.ProficiencyLevel)
                return false;

            return MasteryLevel != other.MasteryLevel;
        }

        /// <summary>
        /// Returns a hash code for this instance.
        /// </summary>
        /// <returns>
        /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. 
        /// </returns>
        public override int GetHashCode()
        {
            return this.Profession.GetHashCode() * this.MasteryLevel * this.ProficiencyLevel;
        }

        /// <summary>
        /// Implements the operator ==.
        /// </summary>
        /// <param name="prof1">The first <see cref="LotroCharacterProfession"/> to compare.</param>
        /// <param name="prof2">The second <see cref="LotroCharacterProfession"/> to compare.</param>
        /// <returns>
        /// 	<c>true</c> if the first <see cref="LotroCharacterProfession"/> is equal to the second <see cref="LotroCharacterProfession"/>; otherwise, <c>false</c>.
        /// </returns>
        public static bool operator ==(LotroCharacterProfession prof1, LotroCharacterProfession prof2)
        {
            return prof1.Equals(prof2);
        }

        /// <summary>
        /// Implements the operator !=.
        /// </summary>
        /// <param name="prof1">The first <see cref="LotroCharacterProfession"/> to compare.</param>
        /// <param name="prof2">The second <see cref="LotroCharacterProfession"/> to compare.</param>
        /// <returns>
        /// 	<c>true</c> if the first <see cref="LotroCharacterProfession"/> is not equal to the second <see cref="LotroCharacterProfession"/>; otherwise, <c>false</c>.
        /// </returns>
        public static bool operator !=(LotroCharacterProfession prof1, LotroCharacterProfession prof2)
        {
            return !prof1.Equals(prof2);
        }
    }

    /// <summary>
    /// Contains price information in terms of Gold, Silver and Copper.
    /// </summary>
    [DebuggerDisplay("{Gold}g, {Silver}s, {Copper}c")]
    public struct LotroItemCost : IEquatable<LotroItemCost>, IComparable<LotroItemCost>
    {
        /// <summary>
        /// Gets an empty <see cref="LotroItemCost"/> structure.
        /// </summary>
        public static readonly LotroItemCost Empty = new LotroItemCost { Copper = 0, Silver = 0, Gold = 0 };

        /// <summary>
        /// Gets or sets the copper amount associated with this cost.
        /// </summary>
        /// <value>An <c>int</c> representing the copper amount.</value>
        public int Copper { get; set; }

        /// <summary>
        /// Gets or sets the silver amount associated with this cost.
        /// </summary>
        /// <value>An <c>int</c> representing the silver amount.</value>
        public int Silver { get; set; }

        /// <summary>
        /// Gets or sets the gold amount associated with this cost.
        /// </summary>
        /// <value>An <c>int</c> representing the gold amount.</value>
        public int Gold { get; set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="LotroItemCost"/> struct.
        /// </summary>
        /// <param name="baseCopper">The base copper value of the item (where 1g = 1000s = 100,000c).</param>
        public LotroItemCost(int baseCopper)
            : this()
        {
            this.Gold = baseCopper / (1000 * 100);
            baseCopper -= this.Gold * (1000 * 100);
            this.Silver = baseCopper / 100;
            baseCopper -= this.Silver * 100;
            this.Copper = baseCopper;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="LotroItemCost"/> struct.
        /// </summary>
        /// <param name="copper">An <c>int</c> representing the copper amount.</param>
        /// <param name="silver">An <c>int</c> representing the silver amount.</param>
        /// <param name="gold">An <c>int</c> representing the gold amount.</param>
        public LotroItemCost(int copper, int silver, int gold)
            : this()
        {
            this.Copper = copper;
            this.Silver = silver;
            this.Gold = gold;
        }

        /// <summary>
        /// Returns a hash code for this instance.
        /// </summary>
        /// <returns>
        /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. 
        /// </returns>
        public override int GetHashCode()
        {
            return this.Copper * this.Silver * this.Gold;
        }

        #region ToString Overloads
        /// <summary>
        /// Returns a <see cref="System.String"/> that represents this instance.
        /// </summary>
        /// <returns>
        /// A <see cref="System.String"/> that represents this instance.
        /// </returns>
        public override string ToString()
        {
            return this.ToString(System.Globalization.CultureInfo.CurrentCulture);
        }

        /// <summary>
        /// Returns a <see cref="System.String"/> that represents this instance.
        /// </summary>
        /// <param name="provider">The provider.</param>
        /// <returns>
        /// A <see cref="System.String"/> that represents this instance.
        /// </returns>
        public string ToString(IFormatProvider provider)
        {
            StringBuilder builder = new StringBuilder();

            if (this.Gold > 0)
                builder.AppendFormat(provider, "{0}g", this.Gold);
            if (this.Silver > 0)
                builder.AppendFormat(provider, " {0}s", this.Silver);
            if (this.Copper > 0)
                builder.AppendFormat(provider, " {0}c", this.Copper);

            return builder.ToString().Trim();
        }

        #endregion

        #region Comparison Operators and Methods
        /// <summary>
        /// Determines whether the specified <see cref="System.Object"/> is equal to this instance.
        /// </summary>
        /// <param name="obj">The <see cref="System.Object"/> to compare with this instance.</param>
        /// <returns>
        /// 	<c>true</c> if the specified <see cref="System.Object"/> is equal to this instance; otherwise, <c>false</c>.
        /// </returns>
        public override bool Equals(object obj)
        {
            if (obj == null || this.GetType() != obj.GetType())
                return false;

            return this.Equals((LotroItemCost)obj);
        }

        /// <summary>
        /// Determines whether the specified <see cref="LotroItemCost"/> is equal to this instance.
        /// </summary>
        /// <param name="other">The <see cref="LotroItemCost"/> to compare with this instance.</param>
        /// <returns>
        /// 	<c>true</c> if the specified <see cref="LotroItemCost"/> is equal to this instance; otherwise, <c>false</c>.
        /// </returns>
        public bool Equals(LotroItemCost other)
        {
            if (this.Copper != other.Copper)
                return false;
            if (this.Silver != other.Silver)
                return false;

            return this.Gold == other.Gold;
        }

        /// <summary>
        /// Implements the operator ==.
        /// </summary>
        /// <param name="first">The first <see cref="LotroItemCost"/> to compare.</param>
        /// <param name="second">The second <see cref="LotroItemCost"/> to compare.</param>
        /// <returns>
        /// 	<c>true</c> if the first <see cref="LotroItemCost"/> is eqaul to the second <see cref="LotroItemCost"/>; otherwise, <c>false</c>.
        /// </returns>
        public static bool operator ==(LotroItemCost first, LotroItemCost second)
        {
            return first.Equals(second);
        }

        /// <summary>
        /// Implements the operator !=.
        /// </summary>
        /// <param name="first">The first <see cref="LotroItemCost"/> to compare.</param>
        /// <param name="second">The second <see cref="LotroItemCost"/> to compare.</param>
        /// <returns>
        /// 	<c>true</c> if the first <see cref="LotroItemCost"/> is not eqaul to the second <see cref="LotroItemCost"/>; otherwise, <c>false</c>.
        /// </returns>
        public static bool operator !=(LotroItemCost first, LotroItemCost second)
        {
            return !first.Equals(second);
        }

        /// <summary>
        /// Implements the operator &gt;.
        /// </summary>
        /// <param name="first">The first <see cref="LotroItemCost"/> to compare.</param>
        /// <param name="second">The second <see cref="LotroItemCost"/> to compare.</param>
        /// <returns>
        /// 	<c>true</c> if the first <see cref="LotroItemCost"/> is greater than the second <see cref="LotroItemCost"/>; otherwise, <c>false</c>.
        /// </returns>
        public static bool operator >(LotroItemCost first, LotroItemCost second)
        {
            return first.CompareTo(second) == 1;
        }

        /// <summary>
        /// Implements the operator &gt;=.
        /// </summary>
        /// <param name="first">The first <see cref="LotroItemCost"/> to compare.</param>
        /// <param name="second">The second <see cref="LotroItemCost"/> to compare.</param>
        /// <returns>
        /// 	<c>true</c> if the first specified <see cref="LotroItemCost"/> is greater than or equal to the second <see cref="LotroItemCost"/>; otherwise, <c>false</c>.
        /// </returns>
        public static bool operator >=(LotroItemCost first, LotroItemCost second)
        {
            return first.Equals(second) || first.CompareTo(second) == 1;
        }

        /// <summary>
        /// Implements the operator &lt;.
        /// </summary>
        /// <param name="first">The first <see cref="LotroItemCost"/> to compare.</param>
        /// <param name="second">The second <see cref="LotroItemCost"/> to compare.</param>
        /// <returns>
        /// 	<c>true</c> if the first specified <see cref="LotroItemCost"/> is less than to the second <see cref="LotroItemCost"/>; otherwise, <c>false</c>.
        /// </returns>
        public static bool operator <(LotroItemCost first, LotroItemCost second)
        {
            return first.CompareTo(second) == -1;
        }

        /// <summary>
        /// Implements the operator &lt;=.
        /// </summary>
        /// <param name="first">The first <see cref="LotroItemCost"/> to compare.</param>
        /// <param name="second">The second <see cref="LotroItemCost"/> to compare.</param>
        /// <returns>
        /// 	<c>true</c> if the first specified <see cref="LotroItemCost"/> is less than or equal to the second <see cref="LotroItemCost"/>; otherwise, <c>false</c>.
        /// </returns>
        public static bool operator <=(LotroItemCost first, LotroItemCost second)
        {
            return first.Equals(second) || first.CompareTo(second) == -1;
        }

        /// <summary>
        /// Compares the current object with another object of the same type.
        /// </summary>
        /// <param name="other">An object to compare with this object.</param>
        /// <returns>
        /// A 32-bit signed integer that indicates the relative order of the objects being compared. The return value has the following meanings:
        /// Value
        /// Meaning
        /// Less than zero
        /// This object is less than the <paramref name="other"/> parameter.
        /// Zero
        /// This object is equal to <paramref name="other"/>.
        /// Greater than zero
        /// This object is greater than <paramref name="other"/>.
        /// </returns>
        public int CompareTo(LotroItemCost other)
        {
            if (this.Gold > other.Gold)
                return 1;
            else if (this.Gold < other.Gold)
                return -1;

            if (this.Silver > other.Silver)
                return 1;
            else if (this.Silver < other.Silver)
                return -1;

            if (this.Copper > other.Copper)
                return 1;
            else if (this.Copper < other.Copper)
                return -1;
            else
                return 0;
        }
        #endregion

        #region Maths Operators
        /// <summary>
        /// Implements the operator +.
        /// </summary>
        /// <param name="first">The first <see cref="LotroItemCost"/> to add.</param>
        /// <param name="second">The second <see cref="LotroItemCost"/> to add.</param>
        /// <returns>The result of the addition.</returns>
        public static LotroItemCost operator +(LotroItemCost first, LotroItemCost second)
        {
            return first.Add(second);
        }

        /// <summary>
        /// Adds a <see cref="LotroItemCost"/> to this instance.
        /// </summary>
        /// <param name="other">The <see cref="LotroItemCost"/> to add.</param>
        /// <returns>The result of the addition</returns>
        public LotroItemCost Add(LotroItemCost other)
        {
            return new LotroItemCost(
                this.Copper + other.Copper,
                this.Silver + other.Silver,
                this.Gold + other.Gold);
        }

        /// <summary>
        /// Implements the operator -.
        /// </summary>
        /// <param name="first">The first <see cref="LotroItemCost"/> to subtract.</param>
        /// <param name="second">The second <see cref="LotroItemCost"/> to subtract.</param>
        /// <returns>The result of the subtraction.</returns>
        public static LotroItemCost operator -(LotroItemCost first, LotroItemCost second)
        {
            return first.Subtract(second);
        }

        /// <summary>
        /// Subtracts a <see cref="LotroItemCost"/> from this instance.
        /// </summary>
        /// <param name="other">The <see cref="LotroItemCost"/> to subtract.</param>
        /// <returns>The result of the subtraction.</returns>
        public LotroItemCost Subtract(LotroItemCost other)
        {
            return new LotroItemCost(
                this.Copper - other.Copper,
                this.Silver - other.Silver,
                this.Gold - other.Gold);
        }

        /// <summary>
        /// Implements the operator *.
        /// </summary>
        /// <param name="first">The first <see cref="LotroItemCost"/> to multiply.</param>
        /// <param name="second">The second <see cref="LotroItemCost"/> to multiply.</param>
        /// <returns>The result of the multiplication.</returns>
        public static LotroItemCost operator *(LotroItemCost first, LotroItemCost second)
        {
            return first.Multiply(second);
        }

        /// <summary>
        /// Multiplies a <see cref="LotroItemCost"/> with this instance.
        /// </summary>
        /// <param name="other">The <see cref="LotroItemCost"/> to multiply.</param>
        /// <returns>The result of the multiplication.</returns>
        public LotroItemCost Multiply(LotroItemCost other)
        {
            return new LotroItemCost(
                this.Copper * other.Copper,
                this.Silver * other.Silver,
                this.Gold * other.Gold);
        }

        /// <summary>
        /// Implements the operator /.
        /// </summary>
        /// <param name="first">The first <see cref="LotroItemCost"/> to divide.</param>
        /// <param name="second">The second <see cref="LotroItemCost"/> to divide.</param>
        /// <returns>The result of the division.</returns>
        public static LotroItemCost operator /(LotroItemCost first, LotroItemCost second)
        {
            return first.Divide(second);
        }

        /// <summary>
        /// Divides this instance by a <see cref="LotroItemCost"/>.
        /// </summary>
        /// <param name="other">The <see cref="LotroItemCost"/> to divide with.</param>
        /// <returns>The result of the division</returns>
        public LotroItemCost Divide(LotroItemCost other)
        {
            return new LotroItemCost(
                (int)Math.Floor((double)this.Copper / other.Copper),
                (int)Math.Floor((double)this.Silver / other.Silver),
                (int)Math.Floor((double)this.Gold / other.Gold));
        }
        #endregion
    }

    
}
